package mathExpEvaluator.model;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import mathExpEvaluator.model.exception.InvalidExpressionException;
import mathExpEvaluator.model.exception.InvalidMathValueException;
import mathExpEvaluator.model.exception.InvalidOperatorException;
import mathExpEvaluator.model.operator.OperatorFactory;

public class ExpressionProcessor {
	private String expression;
	private final OperatorFactory opFactory;
	private final Pattern numberPattern = Pattern.compile("-?\\d+(\\.\\d+)?");
	private final Pattern operatorPattern = Pattern
			.compile("[\\+|\\-|\\*|\\/|\\^|]|SIN|COS");
	private final Pattern finishedBracketPattern = Pattern
			.compile("\\((-?\\d+(\\.\\d+)?)\\)");

	private final Pattern openBracketPattern = Pattern.compile("\\(");
	private final Pattern closeBracketPattern = Pattern.compile("\\)");

	public ExpressionProcessor(String exp, OperatorFactory opFactory) {
		this.expression = exp;
		this.opFactory = opFactory;
	}

	// public List<Operator> getAllOperators() throws InvalidExpressionException
	// {
	// try {
	// List<Operator> ops = new ArrayList<Operator>();
	// Matcher opMatcher = operatorPattern.matcher(this.expression);
	// int position = 0;
	// while (opMatcher.find()) {
	// String exp = opMatcher.group();
	// int bracketCount = getBracketCount(exp, this.expression);
	// Operator op;
	// op = opFactory.createOperator(exp, bracketCount, position++);
	// ops.add(op);
	// }
	// return ops;
	// } catch (InvalidOperatorException e) {
	// // TODO Auto-generated catch block
	// throw new InvalidExpressionException();
	// }
	// }

	public MathValue Calculate() throws InvalidExpressionException {
		try {
			while (!isFinished()) {
				this.expression = removeBracketFromFinishedCalcuation(this.expression);
				System.out.println(this.expression);
				this.expression = doTopOperation(this.expression);
				System.out.println(this.expression);
			}
			return new MathValue(this.expression);
		} catch (InvalidMathValueException exp) {
			throw new InvalidExpressionException();
		}
	}

	private String removeBracketFromFinishedCalcuation(String exp) {
		Matcher matcher = finishedBracketPattern.matcher(exp);
		if (matcher.find()) {
			String number = matcher.group(1);
			return exp.replace(matcher.group(), number);
		} else {
			return exp;
		}
	}

	private boolean isFinished() {
		Matcher matcher = numberPattern.matcher(this.expression);
		if (matcher.matches())
			return true;
		else
			return false;
	}

	public Operant getTopOperant() throws InvalidExpressionException {
		try {
			Operator topOp = null;
			Matcher opMatcher = operatorPattern.matcher(this.expression);
			int position = 0;
			while (opMatcher.find()) {
				String exp = opMatcher.group();
				int bracketCount = getBracketCount(exp, position,
						this.expression);
				Operator op = opFactory.createOperator(opMatcher.group(),
						bracketCount, position++);
				if (topOp == null) {
					topOp = op;
				} else {
					if (op.getPriority().isPriorTo(topOp.getPriority())) {
						topOp = op;
					}
				}
			}
			return topOp.getBelongedOperant(this.expression);
		} catch (InvalidMathValueException exp) {
			throw new InvalidExpressionException();
		} catch (InvalidOperatorException exp) {
			throw new InvalidExpressionException();
		}
	}

	public String doTopOperation(String exp) throws InvalidExpressionException {
		Operant op = getTopOperant();
		MathValue value = op.eval();
		return this.expression.replace(op.toString(), value.toString());
	}

	private int getBracketCount(String exp, int position, String fullExp)
			throws InvalidExpressionException {
		String str = regexReplace(operatorPattern, fullExp, position, "#");
		String[] parts = str.split("#");

		if (netOpenBracketCount(parts[0]) == netCloseBracketCount(parts[1])) {
			return netOpenBracketCount(parts[0]);
		} else {
			throw new InvalidExpressionException();
		}
	}

	private String regexReplace(Pattern p, String text, int index,
			String replacement) {
		Matcher matcher = p.matcher(text);
		int counter = 0;
		while (matcher.find()) {
			if (counter == index) {
				int pos = matcher.start();
				String former = text.substring(0, pos);
				String later = text.substring(pos, text.length());
				Matcher m2 = p.matcher(later);
				return former + m2.replaceFirst(replacement);
			}
			counter++;
		}
		return null;
	}

	private int netCloseBracketCount(String string) {
		return getSymbolCount(closeBracketPattern, string)
				- getSymbolCount(openBracketPattern, string);
	}

	private int netOpenBracketCount(String string) {
		return getSymbolCount(openBracketPattern, string)
				- getSymbolCount(closeBracketPattern, string);
	}

	private int getSymbolCount(Pattern pattern, String text) {
		Matcher matcher = pattern.matcher(text);
		int counter = 0;
		while (matcher.find()) {
			counter++;
		}
		return counter;
	}

	public static void main(String[] args) throws InvalidExpressionException {
		ExpressionProcessor ep = new ExpressionProcessor("(2-1)*(4-1)",
				new OperatorFactory());
		MathValue result = ep.Calculate();
		System.out.println(result.getValue());
	}

}
